# coding:utf-8
# Author : hiicy redldw
# Date : 2019/03/27
from numpy import *

# 生成候选项集
def loadDataSet():
    return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
def createC1(dataSet):# 创建第一个候选项集
    C1=[]
    for transaction in dataSet:
        for item in transaction:
            if not [item] in C1:# 因为要转集合
                C1.append([item])
    C1.sort()
    return list(map(frozenset,C1))
# 数据集扫描
def scanD(D,Ck,minSupport):
    ssCnt={}
    for tid in D:
        for can in Ck:
            if can.issubset(tid):
                if not can in ssCnt.keys():ssCnt[can]=1 # 用frozenset 可以作为字典的键
                else:ssCnt[can]+=1
    numItems = float(len(D))
    retList=[]
    supportData = {}
    for key in ssCnt:
        support = ssCnt[key] / numItems
        if support >= minSupport:
            retList.insert(0,key)
        supportData[key] = support
    return retList,supportData

def aprioriGen(LK,k):# 连接步 生成候选项
    retList = []
    lenLK = len(LK)
    for i in range(lenLK):
        for j in range(i+1,lenLK):
            # 前k-2个项相同时，将两个集合合并
            L1= list(LK[i])[:k-2]
            L2 = list(LK[j])[:k-2]
            L1.sort();L2.sort()
            if L1==L2:
                retList.append(LK[i] | LK[j])
    return retList
def apriori(dataSet,minSupport=0.5):#创造频繁项集
    C1=createC1(dataSet)
    D = list(map(set,dataSet))
    L1,supportData = scanD(D,C1,minSupport)
    L = [L1]
    k = 2
    while len(L[k - 2])>0:
        Ck=aprioriGen(L[k-2],k)
        Lk,supk = scanD(D,Ck,minSupport)
        supportData.update(supk)
        L.append(Lk)
        k += 1
    return L,supportData
dataSet = loadDataSet()
L,supportData = apriori(dataSet)
print(L,'\n',supportData)

# 根据频繁项集寻找关联关系
def generateRules(L,supportData,minConf=0.7): # minConf 可信度
    bigRuleList = []
    for i in range(1,len(L)):
        for freqSet in L[i]:  # 只获取有两个或更多元素的集合
            H1 = [frozenset([item]) for item in freqSet]#每一个物品
            if i > 1:
                rulesFromConseq(freqSet,H1,supportData,bigRuleList,minConf)
            else:
                calConf(freqSet,H1,supportData,bigRuleList,minConf)
    return bigRuleList

# 计算可信度并添加规则
def calConf(freqSet,H,supportData,br1,minConf=0.7):
    prunedH = [] # 空列表
    for conseq in H:
        # p -> H;freqSet - conseq = P ,freqSet = H+P ,conseq = H
        conf = supportData[freqSet] / supportData[freqSet - conseq]
        if conf >= minConf:
            print(freqSet - conseq, '--->',conseq,'conf: ',conf)
            br1.append((freqSet - conseq,conseq,conf))
            prunedH.append(conseq) # 后件~规则~频繁项的寻找
    return prunedH

# 根据规则进行分级操作，
def rulesFromConseq(freqSet,H,supportData,br1,minConf=0.7):
    m = len(H[0])
    if len(freqSet) > (m+1):  # 最后是一对多的情况 就不用再添加规则呢
        Hmp1 = aprioriGen(H,m+1)
        # freqset不变 通过-Hmp1得到前件
        # 创建Hm+1条新候选规则
        Hmp1 = calConf(freqSet,Hmp1,supportData,br1,minConf)
        if len(Hmp1) > 1:
            rulesFromConseq(freqSet,Hmp1,supportData,br1,minConf)
print('\n\n\n')
rules = generateRules(L,supportData,minConf=0.5)
print('\n\n\n',rules)

# 发现国会投票中的模式;目标:从美国国会获得一些最新的投票记录并基于这些数据来尝试学习一些关联规则
from time import sleep
from votesmart import votesmart
votesmart.apikey = 'get your api key first'
def getActionIds():
    actionIdList = []; billTitleList = []
    fr = open('recent20bills.txt')
    for line in fr.readlines():
        billNum = int(line.split('\t')[0])
        try:
            billDetail = votesmart.votes.getBill(billNum) #api call
            for action in billDetail.actions:
                if action.level == 'House' and \
                (action.stage == 'Passage' or action.stage == 'Amendment Vote'):
                    actionId = int(action.actionId)
                    print('bill: %d has actionId: %d' % (billNum, actionId))
                    actionIdList.append(actionId)
                    billTitleList.append(line.strip().split('\t')[1])
        except:
            print("problem getting bill %d" % billNum)
        sleep(1)                                      #delay to be polite
    return actionIdList, billTitleList

# 创建一个事务数据库
def getTransList(actionIdList, billTitleList): #this will return a list of lists containing ints
    itemMeaning = ['Republican', 'Democratic']#list of what each item stands for
    for billTitle in billTitleList:#fill up itemMeaning list
        itemMeaning.append('%s -- Nay' % billTitle)
        itemMeaning.append('%s -- Yea' % billTitle)
    transDict = {}#list of items in each transaction (politician)
    voteCount = 2
    for actionId in actionIdList:
        sleep(3)
        print('getting votes for actionId: %d' % actionId)
        try:
            voteList = votesmart.votes.getBillActionVotes(actionId)
            for vote in voteList:
                if not transDict.has_key(vote.candidateName):
                    transDict[vote.candidateName] = []
                    if vote.officeParties == 'Democratic':
                        transDict[vote.candidateName].append(1)
                    elif vote.officeParties == 'Republican':
                        transDict[vote.candidateName].append(0)
                if vote.action == 'Nay':
                    transDict[vote.candidateName].append(voteCount)
                elif vote.action == 'Yea':
                    transDict[vote.candidateName].append(voteCount + 1)
        except:
            print("problem getting actionId: %d" % actionId)
        voteCount += 2
    return transDict, itemMeaning
# 发现毒蘑菇的相似特征
mushDatSet = [line.split() for line in open(r'F:\Resources\Dataset\mushroom.dat').readlines()]
L,suppData = apriori(mushDatSet,minSupport=0.3)
for item in L[1]:
    if item.intersection('2'):print(item)